---
title: Tutorial - Ampliar con colecciones de contenido.
description: >-
  Convierte el código del tutorial de Crear un Blog cambiando el contenido de los archivos a colecciones de contenido.
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Box from '~/components/tutorial/Box.astro';
import MultipleChoice from '~/components/tutorial/MultipleChoice.astro';
import PreCheck from '~/components/tutorial/PreCheck.astro';
import Option from '~/components/tutorial/Option.astro';
import { Steps } from '@astrojs/starlight/components';

Las **colecciones de contenido** son una forma poderosa de gestionar grupos de contenido similar, como lo son las publicaciones de un blog. Las colecciones ayudan a organizar tus documentos, validar el contenido de los archivos YAML y proveen seguridad de tipos con TypeScript automáticamente para todo tu contenido (incluso si no escribes nada de TypeScript por tu cuenta).

<PreCheck>
  - Mover el directorio de publicaciones del blog en el directorio `src/content/`
  - Crear un esquema que defina el contenido de la publicación en términos de frontmatter.
  - Utilizar el método `getCollection()` para obtener el contenido de tus publicaciones y sus metadatos.
</PreCheck>

## Pre-requisitos

Necesitarás **un proyecto existente de Astro que incluya archivos Markdown o MDX en el directorio  `src/pages/`**.

Este tutorial utiliza [el proyecto de Crear un Blog terminado](https://github.com/withastro/blog-tutorial-demo) para demostrar cómo convertir un blog en colecciones de contenido. Puedes clonar y usar la base de código localmente, o completar el tutorial en tu navegador [editando el código del tutorial del blog en StackBlitz](https://stackblitz.com/github/withastro/blog-tutorial-demo/tree/complete?file=src%2Fpages%2Findex.astro).

Si lo prefieres, puedes seguir los pasos usando tus propios proyectos, pero, necesitarás realizar algunos ajustes en las instrucciones del código.

Nosotros recomendamos usar nuestro proyecto de ejemplo para terminar este tutorial corto. Después, puedes utilizar lo aprendido para crear colecciones de contenido en tu proyecto.

## Código de Construir un Blog

En el [tutorial introductorio de Crear un Blog](/es/tutorial/0-introduction/), aprendiste acerca del [sistema de rutas basado en archivos](/es/guides/routing/#rutas-estáticas): cualquier archivo `.astro`, `.md`, o `.mdx` dentro del directorio `src/pages/` se convertirá automáticamente en una página en tu sitio.

Para crear tu primera publicación `https://example.com/posts/post-1/`, creaste el directorio `/posts/` y agregaste el archivo `post-1.md`. Después, agregaste un archivo Markdown en este directorio cada vez que querías agregar una nueva publicación en tu blog.

## Páginas vs Colecciones

Incluso usando colecciones de contenido deberás usar el directorio `src/pages/` para cada página individual, tal como la página “Acerca De”. Pero, mover el contenido de tus publicaciones al directorio `src/content/` te permitirá utilizar APIs con más características y de mejor rendimiento para generar el indexado de tu blog y mostrar las publicaciones de manera individual.

Al mismo tiempo, recibirás guía y dispondrás autocompletado en tu editor de código porque usarás un **[esquema](/es/guides/content-collections/#definiendo-un-esquema-de-colección)** para definir la estructura común para cada publicación que Astro te ayudará a implementar. En tu esquema, puedes especificar las propiedades de frontmatter que son necesarias, como la descripción, el autor, y que tipo de dato debe tener cada propiedad, por ejemplo, si es una cadena de texto o un arreglo. Esto ayuda a evitar muchos errores de manera temprana, usando errores descriptivos que te dirán cual es el problema exacto.

Lee más sobre [las colecciones de contenido de Astro](/es/guides/content-collections/) en nuestra guía, o comienza con las instrucciones a continuación para convertir un blog básico de `src/pages/posts/` a `src/content/posts/`.

<Box icon="question-mark">
### Prueba tu conocimiento

1. ¿Qué tipo de página guardarías en el directorio `src/pages/`?

    <MultipleChoice>
      <Option>
        Publicaciones de un blog que comparten la misma estructura y metadatos.
      </Option>
      <Option>
        Páginas de producto de una tienda en línea (eCommerce).
      </Option>
      <Option isCorrect>
        Una página de contacto, porque no puedes tener páginas similares del mismo tipo.
      </Option>
    </MultipleChoice>

2. ¿Cuál **no** es una ventaja de mover las publicaciones de un blog a una colección de contenido?

    <MultipleChoice>
      <Option isCorrect>
         Las páginas son creadas para cada archivo automáticamente.
      </Option>
      <Option>
        Mejores mensajes de error porque Astro sabe más de cada archivo.
      </Option>
      <Option>
        Mejor forma de obtener los datos, con funciones de mayor rendimiento.
      </Option>
    </MultipleChoice>

3. Las colecciones de contenido usan TypeScript...
    <MultipleChoice>
      <Option>
        Para hacerme sentir mal
      </Option>
      <Option isCorrect>
        Para entender mi proyecto, incluso si no escribo nada de TypeScript
      </Option>
      <Option>
        Solo si tengo la configuración estricta (`strict`) o la más estricta (`strictest`).
      </Option>
    </MultipleChoice>

</Box>

## Extendiendo el tutorial del blog con colecciones de contenido.

Los siguientes pasos mostrarán cómo extender el resultado del tutorial Crear un Blog creando colecciones de contenido para las publicaciones del blog.

### Actualiza las dependencias

<Steps>
1. Actualiza a la última versión de Astro y actualiza todas las integraciones a sus últimas versiones ejecutando los siguiente comandos en tu terminal:

    <PackageManagerTabs>
      <Fragment slot="npm">
      ```shell
      # Actualiza a Astro v4.x
      npm install astro@latest

      # Ejemplo: Actualiza la integración de Preact
      npm install @astrojs/preact@latest
      ```
      </Fragment>
      <Fragment slot="pnpm">
      ```shell
      # Actualiza a Astro v4.x
      pnpm add astro@latest

      # Ejemplo: Actualiza la integración de Preact
      pnpm add @astrojs/preact@latest
      ```
      </Fragment>
      <Fragment slot="yarn">
      ```shell
      # Actualiza a Astro v4.x
      yarn add astro@latest

    # Ejemplo: Actualiza la integración de Preact
      yarn add @astrojs/preact@latest
      ```
      </Fragment>
    </PackageManagerTabs>

    :::tip
    Si estás utilizando tu propio proyecto, asegúrate de actualizar cualquier dependencia que tengas instalada. El código del tutorial solo utiliza la integración de Preact.
    :::

2. El blog utiliza la configuración `base` de TypeScript (la menos estricta). Para utilizar las colecciones de contenido debes [configurar TypeScript](/es/guides/content-collections/#configurando-typescript), **también** deberás usar la configuración estricta `strict` o la más estricta `strictest`, **o** agregar dos opciones en el archivo `tsconfig.json`.

    Para utilizar colecciones de contenido sin escribir nada de TypeScript, agrega las siguientes dos líneas al archivo de configuración:

    ```json title="tsconfig.json" ins={5,6}
    {
      // Nota: No se necesitan cambios si se utiliza "astro/tsconfigs/strict" o "astro/tsconfigs/strictest"
      "extends": "astro/tsconfigs/base",
      "compilerOptions": {
        "strictNullChecks": true,
        "allowJs": true
      }
    }
    ```
</Steps>

### Crear una colección para tus publicaciones del blog.

<Steps>
3. Crea una nueva **colección** (directorio) llamado `src/content/posts/`.

4. Mueve todas las publicaciones de tu blog (archivos `.md`) de `src/pages/posts/` en esta nueva colección.

5. Crea el archivo `src/content/config.ts` para [definir un esquema](/es/guides/content-collections/#definiendo-un-esquema-de-colección) para la colección `postsCollection`. Para el código que existe en este tutorial, agrega el siguiente contenido al archivo para definir todas las propiedades del frontmatter utilizadas en las entradas de blog:

    ```ts title="src/content/config.ts"
    // Importa las utilidades de `astro:content`
    import { z, defineCollection } from "astro:content";
    // Define un `type` y un `schema` para cada colección
    const postsCollection = defineCollection({
        type: 'content',
        schema: z.object({
          title: z.string(),
          pubDate: z.date(),
          description: z.string(),
          author: z.string(),
          image: z.object({
            url: z.string(),
            alt: z.string()
          }),
          tags: z.array(z.string())
        })
    });
    // Exporta un solo objeto `collections` con las colecciones registradas
    export const collections = {
      posts: postsCollection,
    };
    ```

6. Para que astro reconozca tu esquema, abandona el servidor de desarrollo (`CTRL + C`) y ejecuta el siguiente comando [`npx astro sync`](/es/reference/cli-reference/#astro-sync). Esto definirá el módulo `astro:content` para la API de Colecciones de Contenido. Reinicia el servidor de desarrollo para continuar con el tutorial. 
</Steps>

### Generando las páginas de una colección

<Steps>
7. Crea un archivo de página llamado `src/pages/posts/[...slug].astro`. Los archivos Markdown y MDX no se convertirán en páginas automáticamente usando el sistema de rutas basado en archivos cuando estos viven en una colección, por eso debes crear una página encargada de generar un blog individual para cada publicación.

8. Agrega el siguiente código para [consultar tu colección](/es/guides/content-collections/#consultando-colecciones), para hacer que el slug y el contenido de cada publicación del blog estén disponibles para cada página que se genere:

    ```astro title="src/pages/posts/[...slug].astro"
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';

    export async function getStaticPaths() {
      const blogEntries = await getCollection('posts');
      return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
      }));
    }

    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
    ```

9. La etiqueta `<Content />` renderiza tu publicación con el `layout` para archivos Markdown. Esto te permite especificar una plantilla común para todas tus publicaciones.

    ```astro title="src/pages/posts/[...slug].astro" ins={15-17}
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';

    export async function getStaticPaths() {
      const blogEntries = await getCollection('posts');
      return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
      }));
    }

    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
    <MarkdownPostLayout frontmatter={entry.data}>
      <Content />
    </MarkdownPostLayout>
    ```

10. Elimina la definición de `layout` del frontmatter de cada publicación. Ahora, tu contenido está envuelto en un layout al renderizarse, y esta propiedad ya no es necesaria.

    ```md title="src/content/posts/post-1.md" del={2}
    ---
    layout: ../../layouts/MarkdownPostLayout.astro
    title: 'Mi primera entrada en el blog'
    pubDate: 2022-07-01
    ...
    ---
    ```
</Steps>

### Reemplaza `Astro.glob()` con `getCollection()`

<Steps>
11. En cualquier lugar donde tengas una lista de publicaciones de blog, como la página de blog del tutorial (`src/pages/blog.astro/`), deberás reemplazar `Astro.glob()` con [`getCollection()`](/es/reference/api-reference/#getcollection) como la forma de obtener contenido y metadatos de tus archivos Markdown.

      ```astro title="src/pages/blog.astro" "post.data" "getCollection(\"posts\")" "/posts/${post.slug}/" del={7} ins={2,8}
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";

    const pageTitle = "Mi blog de Astro sobre aprendizaje";
    const allPosts = await Astro.glob("../pages/posts/*.md");
    const allPosts = await getCollection("posts");
    ---

12. También necesitarás actualizar las referencias de los datos regresados por cada `post`. Ahora encontrarás los valores de frontmatter en la propiedad `data` de cada objeto. Adicional a esto, cuando utilizas colecciones, cada objeto `post` tendrá un `slug` por página, no una URL completa.

    ```astro title="src/pages/blog.astro" "data" "/posts/$\{post.slug\}/" del={14} ins={15}
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";

    const pageTitle = "My Astro Learning Blog";
    const allPosts = await getCollection("posts");
    ---
    <BaseLayout pageTitle={pageTitle}>
      <p>Aquí es donde voy a publicar sobre mi viaje de aprendizaje con Astro.</p>
      <ul>
        {
          allPosts.map((post) => (
            <BlogPost url={post.url} title={post.frontmatter.title} />)}
            <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} />
          ))
        }
      </ul>
    </BaseLayout> 
    ```

13. El tutorial del proyecto de blog también genera dinámicamente una página para cada etiqueta `src/pages/tags/[tag].astro` y muestra una lista de etiquetas `src/pages/tags/index.astro`. 
   
          Aplica los mismos cambios de arriba a los siguientes dos archivos:
      
          - Obtén los datos de toda tu colección de publicaciones usando `getCollection("posts")` en vez de usar `Astro.glob()`
          - Accede a las propiedades de frontmatter a través del objeto `data` en vez de `frontmatter`
          - Crea una URL de página agregando el `slug` de la publicación a la ruta `/posts/`

        La página encargada de gestionar las páginas individuales para cada etiqueta ahora tendrá los siguientes cambios:

        ```astro title="src/pages/tags/[tag].astro" "post.data.tags" "getCollection(\"posts\")" "post.data.title" ins={2} "/posts/${post.slug}/"
        ---
        import { getCollection } from "astro:content";
        import BaseLayout from "../../layouts/BaseLayout.astro";
        import BlogPost from "../../components/BlogPost.astro";

        export async function getStaticPaths() {
          const allPosts = await getCollection("posts");
          const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())];

          return uniqueTags.map((tag) => {
            const filteredPosts = allPosts.filter((post) =>
              post.data.tags.includes(tag)
            );
            return {
              params: { tag },
              props: { posts: filteredPosts },
            };
          });
        }
        
        const { tag } = Astro.params;
        const { posts } = Astro.props;
        ---

        <BaseLayout pageTitle={tag}>
          <p>Artículos con la etiqueta {tag}</p>
          <ul>
            { posts.map((post) => <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} />) }
          </ul>            
        </BaseLayout>
        ```

        <Box icon="puzzle-piece">
          ### Inténtalo tú mismo - Actualiza la consulta en la página de indexado de etiquetas.

          Importa y utiliza `getCollection` para obtener las etiquetas en las publicaciones del blog en `src/pages/tags/index.astro` siguiendo [los mismos pasos de arriba](#reemplaza-astroglob-con-getcollection).

          <details>
          <summary>Muéstrame el código.</summary>
          ```astro title="src/pages/tags/index.astro" "post.data" "getCollection(\"posts\")" ins={2}
          ---
          import { getCollection } from "astro:content";
          import BaseLayout from "../../layouts/BaseLayout.astro";     
          const allPosts = await getCollection("posts");
          const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
          const pageTitle = "Índice de etiquetas";
          ---
          ...
          ```
          </details>
      </Box>
</Steps>

### Actualiza cualquier valor del frontmatter para que coincida con tu esquema

<Steps>
14. Si es necesario, actualiza los valores del frontmatter que no coincidan con el esquema de tu colección en todo tu proyecto, al igual que en tu plantilla o `layout`.

    En el ejemplo tutorial de blog, `pubDate` era una cadena de texto. Ahora, con el nuevo esquema definido para frontmatter, `pubDate` será un objeto de tipo `Date`.
    
    Para renderizar la fecha en la plantilla de la publicación, necesitamos convertirla a cadena de texto:

    ```astro title="src/layouts/MarkdownPostLayout.astro" ins="toString()"
    ...
    <BaseLayout pageTitle={frontmatter.title}>
      <p>{frontmatter.pubDate.toString().slice(0,10)}</p>
      <p><em>{frontmatter.description}</em></p>
      <p>Escrito por: {frontmatter.author}</p>
      <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />
    ...
    ```
</Steps>

### Actualiza la función RSS

<Steps>
15. Finalmente, el tutorial de blog incluye el feed RSS. Esta función también debe utilizar `getCollection()` para regresar la información de tus publicaciones. También deberás generar los artículos utilizando la propiedad `data`.

    ```js title="src/pages/rss.xml.js" del={2,11} ins={3,6,12-17}
    import rss from '@astrojs/rss';
    import { pagesGlobToRssItems } from '@astrojs/rss';
    import { getCollection } from 'astro:content';

    export async function GET(context) {
      const posts = await getCollection("posts");
      return rss({
        title: 'Aprendiz de Astro | Blog',
        description: 'Mi viaje aprendiendo Astro',
        site: context.site,
        items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
        items: posts.map((post) => ({
          title: post.data.title,
          pubDate: post.data.pubDate,
          description: post.data.description,
          link: `/posts/${post.slug}/`,
        })),
        customData: `<language>en-us</language>`,
      })
    }
    ```
</Steps>

Para ver el ejemplo completo del tutorial usando colecciones de contenido, [consulta la rama de Colecciones de Contenido](https://github.com/withastro/blog-tutorial-demo/tree/content-collections) del repositorio del tutorial.
